package org.artifact.protocol;

import java.io.File;
import java.io.FileFilter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import org.artifact.protocol.convert.JavaConvert;
import org.artifact.protocol.enums.ProtocolConstant;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.template.Template;
import cn.hutool.extra.template.TemplateConfig;
import cn.hutool.extra.template.TemplateConfig.ResourceMode;
import cn.hutool.extra.template.TemplateEngine;
import cn.hutool.extra.template.TemplateUtil;
import lombok.Builder;

@Builder
public class ProtocolBuilder {
	/** 模板组 */
	private TemplateConfig templateConfig;

	/** 生成的源码路径 */
	private String sourcePath;

	/** 生成源码的包名称 */
	private String packageName;

	/** 设计文件路径 */
	private String designSourcePath;

	/**
	 * 使用默认模板组
	 * 
	 * @return
	 */
	public void useDefaultGroupTemplate() {
		this.templateConfig = new TemplateConfig();
		this.templateConfig.setCharset(CharsetUtil.CHARSET_UTF_8);
		this.templateConfig.setResourceMode(ResourceMode.CLASSPATH);
	}

	public void exec() throws Exception {
		if (this.templateConfig == null) {
			useDefaultGroupTemplate();
		}

		if (StrUtil.isBlank(sourcePath) || StrUtil.isBlank(packageName) || designSourcePath == null) {
			throw new IllegalStateException("请检查配置");
		}

		check();

		// verify();

		execute();

		completeInfo();
	}

	private void execute() {
		TemplateEngine engine = TemplateUtil.createEngine(this.templateConfig);

		List<File> files = FileUtil.loopFiles(designSourcePath, new FileFilter() {
			@Override
			public boolean accept(File pathname) {
				return pathname.getName().endsWith(".protocol");
			}
		});

		
		List<ProtocolModule> modules = new ArrayList<>();
		
		List<ProtocolStruct> protocolStructs = new ArrayList<>();
		
		List<ProtocolEnum> protocolEnums = new ArrayList<>();
		
		for (File file : files) {
			List<ProtocolSend> protocolSends = new ArrayList<>();
			List<ProtocolPush> protocolPushs = new ArrayList<>();
			
			
			List<String> lines = FileUtil.readUtf8Lines(file);
			AtomicInteger lineNumber = new AtomicInteger(0);
			ProtocolResolve resolve = new ProtocolResolve(new JavaConvert());
			String classPath = packageName+"."+ProtocolConstant.STRUCT.getPath();
			String beforeLine = "";
			for (int i = lineNumber.get(); i < lines.size();i=lineNumber.incrementAndGet()) {
				String line = lines.get(i);
				line = StrUtil.trim(line);
				// 解析
				if (line.startsWith(ProtocolConstant.SEND.getName())) {
					protocolSends.add(resolve.resolveSend(classPath,beforeLine,line));
				} else if (line.startsWith(ProtocolConstant.PUSH.getName())) {
					protocolPushs.add(resolve.resolvePush(classPath,beforeLine,line));
				} else if (line.startsWith(ProtocolConstant.STRUCT.getName())) {
					protocolStructs.add(resolve.resolveStruct(classPath,beforeLine,line, lines, lineNumber));
				} else if (line.startsWith(ProtocolConstant.ENUM.getName())) {
					protocolEnums.add(resolve.resolveEnum(beforeLine,line, lines, lineNumber));
				}
				beforeLine = line;
			}
			
			
			if (CollUtil.isNotEmpty(protocolSends) || CollUtil.isNotEmpty(protocolPushs)) {
				String name = StrUtil.removeSuffixIgnoreCase(file.getName(), ".protocol");
				short id = (short) (name.hashCode() & Short.MAX_VALUE);
				ProtocolModule module = new ProtocolModule(id, StrUtil.upperFirst(name), protocolSends, protocolPushs);
				Template template = engine.getTemplate(ProtocolConstant.SEND.getBtl());
				
				
				Map<String, Object> bindingMap = new HashMap<>();
				bindingMap.put("module", module);
				bindingMap.put("packageName", packageName);
				String path = sourcePath + File.separator + ProtocolConstant.SEND.getPath() + File.separator + module.getName() + "Module.java";
				render(path, template, bindingMap);
				modules.add(module);
			}
		}
		
		
		if (CollUtil.isNotEmpty(protocolStructs)) {
			Template template = engine.getTemplate(ProtocolConstant.STRUCT.getBtl());
			
			Map<String, Object> bindingMap = new HashMap<>();
			bindingMap.put("packageName", packageName);
			
			
			for (ProtocolStruct protocolStruct : protocolStructs) {
				bindingMap.put("struct", protocolStruct);
				String path = sourcePath + File.separator + ProtocolConstant.STRUCT.getPath() + File.separator + protocolStruct.getStructName() + ".java";
				render(path, template, bindingMap);
			}
		}
		
		if (CollUtil.isNotEmpty(protocolEnums)) {
			Template template = engine.getTemplate(ProtocolConstant.ENUM.getBtl());
			Map<String, Object> bindingMap = new HashMap<>();
			bindingMap.put("packageName", packageName);
			
			
			for (ProtocolEnum protocolEnum : protocolEnums) {
				bindingMap.put("enum", protocolEnum);
				String path = sourcePath + File.separator + ProtocolConstant.ENUM.getPath() + File.separator + protocolEnum.getEnumName() + ".java";
				render(path, template, bindingMap);
			}
		}
		
		
		// 生成LUA
		if (true) {
			Template template = engine.getTemplate("lua.btl");
			Map<String, Object> bindingMap = new HashMap<>();
			bindingMap.put("structs", protocolStructs);
			bindingMap.put("enums", protocolEnums);
			bindingMap.put("modules", modules);
			String path = sourcePath + File.separator + "NetworkProtocol.lua";
			render(path, template, bindingMap);
		}

		// 生成工具类
		Template template = engine.getTemplate("struct_util.btl");
		Map<String, Object> bindingMap = new HashMap<>();
		bindingMap.put("packageName", packageName);
		bindingMap.put("structs", protocolStructs);
		String path = sourcePath + File.separator + "StructUtil.java";
		render(path, template, bindingMap);
	}

	private void render(String path,Template template, Map<String, Object> bindingMap) {
		FileWriter fileWriter = null;
		try {
			fileWriter = new FileWriter(new File(path));
			template.render(bindingMap, fileWriter);
			fileWriter.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void check() {
		ProtocolConstant[] enums = ProtocolConstant.values();
		for (ProtocolConstant btlEnum : enums) {
			File file = new File(sourcePath + File.separator + btlEnum.getPath());
			if (!file.exists() || !file.isDirectory()) {
				file.mkdirs();
			}
		}
	}

	/**
	 * 成功打印信息
	 */
	public static void completeInfo() {
		System.out.println("*********************************************");
		System.out.println("*                                           *");
		System.out.println("**********代码生成完成-请刷新项目！**********");
		System.out.println("*                                           *");
		System.out.println("*********************************************");
		System.exit(0);
	}
}
